R Notebook

library(broom)
library(bslib)

Attaching package: ‘bslib’

The following object is masked from ‘package:broom’:

    bootstrap

The following object is masked from ‘package:utils’:

    page
library(dplyr)

Attaching package: ‘dplyr’

The following objects are masked from ‘package:stats’:

    filter, lag

The following objects are masked from ‘package:base’:

    intersect, setdiff, setequal, union
library(geojsonio)
Registered S3 method overwritten by 'geojsonsf':
  method        from   
  print.geojson geojson

Attaching package: ‘geojsonio’

The following object is masked from ‘package:base’:

    pretty
library(ggnewscale)
library(ggplot2)
library(leaflet)
Registered S3 method overwritten by 'htmlwidgets':
  method           from         
  print.htmlwidget tools:rstudio
library(osmdata)
Data (c) OpenStreetMap contributors, ODbL 1.0. https://www.openstreetmap.org/copyright
library(RColorBrewer)
library(RSocrata)
library(rstudioapi)
library(sf)
Linking to GEOS 3.10.2, GDAL 3.4.2, PROJ 8.2.1; sf_use_s2() is TRUE
library(shiny)

Attaching package: ‘shiny’

The following object is masked from ‘package:geojsonio’:

    validate
library(tidyverse)
Registered S3 methods overwritten by 'dbplyr':
  method         from
  print.tbl_lazy     
  print.tbl_sql      
── Attaching packages ────────────────────────────────── tidyverse 1.3.2 ──✔ tibble  3.1.8     ✔ purrr   0.3.4
✔ tidyr   1.2.1     ✔ stringr 1.4.0
✔ readr   2.1.2     ✔ forcats 0.5.2── Conflicts ───────────────────────────────────── tidyverse_conflicts() ──
✖ bslib::bootstrap() masks broom::bootstrap()
✖ dplyr::filter()    masks stats::filter()
✖ dplyr::lag()       masks stats::lag()
library(wesanderson)

# Set working directory
current_path <- rstudioapi::getActiveDocumentContext()$path
setwd(dirname(current_path))
getwd()
[1] "/Users/xxxxoxygene/Downloads/Columbia University/Fall2022/STAT4243/Project 2/fall2022-project2-group2/doc"
# Load the restaurant dataset
data <- read.socrata(
  "https://data.cityofnewyork.us/resource/43nn-pn8j.json",
  app_token = "zTRehp1897SQtpYtBiIOUMfR4"
)

# Extract years
data$year <- format(data$inspection_date,"%Y")

# Filter the dataset
df <- data %>%
  filter(data$year >= 2019 & zipcode != "" & dba != "") %>%
  mutate(grade = replace(grade, grade == "", NA))

head(df)
# Read the geojson file containing spatial info
spdf_file <- geojson_read("../data/zip_code_040114.geojson", what = "sp")

stats_df <- spdf_file@data

# Convert it to a spatial data frame, with zip code as index
spdf_data <- tidy(spdf_file,
  region = "ZIPCODE"  # Use ZIPCODE variable as index, the index will be named "id"
)

Section I: Filtered plots

# Number of actions over time
df %>%
  group_by(year, action) %>%
  summarize(num_violations = n()) %>%
  ggplot(aes(x = year, y = num_violations, fill = action)) +
  geom_bar(stat = "identity") +
  labs(title = "Number of Actions over Time", x = "Year", y = "Count", fill = "Action") +
  scale_fill_manual(values = wes_palettes$Moonrise3[c(3, 4, 5, 1, 2)]) +
  theme_minimal() +
  theme(legend.position = "bottom") +
  guides(fill = guide_legend(nrow = 5, byrow = TRUE))
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

  
# Number of violations by critical level over time
df %>%
  filter(critical_flag != "Not Applicable") %>%
  group_by(year, critical_flag) %>%
  summarize(num_violations = n()) %>%
  ggplot(aes(x = year, y = num_violations, fill = critical_flag)) +
  geom_bar(stat = "identity") +
  labs(title = "Number of Violations by Critical Level over Time", x = "Year", y = "Count", fill = "Critical Level") +
  theme_minimal() +
  theme(legend.position = "bottom")
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

# Top 5 violations
df %>%
  group_by(year, violation_code) %>%
  summarize(num_violations = n()) %>%
  ungroup() %>%
  group_by(year) %>%
  slice_max(num_violations, n = 5) %>%
  ggplot(aes(x = year, y = num_violations, fill = violation_code)) +
  geom_bar(stat = "identity") +
  labs(title = "Top 5 Violations over Time", x = "Year", y = "Count", fill = "Violation Code") +
  scale_fill_brewer(palette = "Set2") +
  theme_minimal() +
  theme(legend.position = "bottom")
`summarise()` has grouped output by 'year'. You can override using the `.groups` argument.

Section II: Interactive plots

# Non-interactive map (population by region)
ggplot() +
  geom_polygon(data = spdf_data %>% left_join(stats_df, c("id" = "ZIPCODE")),
               aes(x = long, y = lat, group = group, fill = POPULATION),
               color = "white", size = .2) +
  theme_void() +
  coord_map() +
  scale_fill_distiller(palette = "YlGnBu", direction = 1) +
  labs(title = "Population in New York City",
       subtitle = "Neighborhoods are filled by population",
       fill = "Population")

# Number of restaurants per zipcode
Num_Rest_Code <- df %>%
  group_by(zipcode, dba, latitude, longitude) %>%
  count() %>%
  group_by(zipcode) %>%
  count()

Critical_2019_by_Code <- df %>%
  filter(year == 2019) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Critical_2020_by_Code <- df %>%
  filter(year == 2020) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Critical_2021_by_Code <- df %>%
  filter(year == 2021) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Critical_2022_by_Code <- df %>%
  filter(year == 2022) %>%
  group_by(zipcode) %>%
  summarize(Total = n())

Critical_spdf_file_2022 <- spdf_file
Critical_spdf_file_2022@data <- Critical_spdf_file_2022@data %>%
  left_join(Critical_2022_by_Code, c("ZIPCODE" = "zipcode"))

Critical_spdf_file_2019 <- spdf_file
Critical_spdf_file_2019@data <- Critical_spdf_file_2019@data %>%
  left_join(Critical_2019_by_Code, c("ZIPCODE" = "zipcode"))

Critical_spdf_file_2020 <- spdf_file
Critical_spdf_file_2020@data <- Critical_spdf_file_2020@data %>%
  left_join(Critical_2020_by_Code, c("ZIPCODE" = "zipcode"))

Critical_spdf_file_2021 <- spdf_file
Critical_spdf_file_2021@data <- Critical_spdf_file_2021@data %>%
  left_join(Critical_2021_by_Code, c("ZIPCODE" = "zipcode"))

critical_violations <- list(Critical_spdf_file_2019, Critical_spdf_file_2020, Critical_spdf_file_2021, Critical_spdf_file_2022)
names(critical_violations) <- c("2019", "2020", "2021", "2022")

nc_pal <- colorNumeric(palette = "YlOrBr", domain = Critical_spdf_file_2019@data$Total, na.color = 'transparent')

leaflet() %>%
  addProviderTiles("CartoDB") %>%
  # First layer of polygons
  addPolygons(
    data = Critical_spdf_file_2022,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0('Total Critical Violation : ', Total),
    group = '2022',
    highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
  ) %>%
  # Second layer of polygons
  addPolygons(
    data = Critical_spdf_file_2021,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Critical Violation : ', Total),
    group = '2021',
    highlight = highlightOptions(weight = 3, color = "red", bringToFront =  TRUE)
  ) %>%
  addLayersControl(overlayGroups = c("2022", "2021")) %>%
  addLegend(pal = nc_pal, values = Critical_spdf_file_2022$Total, opacity = 0.9, title = "Critical", position = "bottomleft")
Warning: Some values were outside the color scale and will be treated as NAWarning: Some values were outside the color scale and will be treated as NA
Total_violation <- df %>%
  group_by(zipcode) %>%
  summarize(Total = n())

Total_2019_by_Code <- df %>%
  filter(year == 2019) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Total_2020_by_Code <- df %>%
  filter(year == 2020) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Total_2021_by_Code <- df %>%
  filter(year == 2021) %>%
  group_by(zipcode) %>%
  summarize(Total = n())
  
Total_2022_by_Code <- df %>%
  filter(year == 2022) %>%
  group_by(zipcode) %>%
  summarize(Total = n())

Total_spdf_file_2022 <- spdf_file
Total_spdf_file_2022@data <- Total_spdf_file_2022@data %>%
  left_join(Total_2022_by_Code, c("ZIPCODE" = "zipcode"))

Total_spdf_file_2019 <- spdf_file
Total_spdf_file_2019@data <- Total_spdf_file_2019@data %>%
  left_join(Total_2019_by_Code, c("ZIPCODE" = "zipcode"))

Total_spdf_file_2020 <- spdf_file
Total_spdf_file_2020@data <- Total_spdf_file_2020@data %>%
  left_join(Total_2020_by_Code, c("ZIPCODE" = "zipcode"))

Total_spdf_file_2021 <- spdf_file
Total_spdf_file_2021@data <- Total_spdf_file_2021@data %>%
  left_join(Total_2021_by_Code, c("ZIPCODE" = "zipcode"))

total_violation <- list(Total_spdf_file_2019, Total_spdf_file_2020, Total_spdf_file_2021, Total_spdf_file_2022)
names(total_violation) <- c("2019", "2020", "2021", "2022")

nc_pal <- colorNumeric(palette = "YlOrBr", domain = Total_spdf_file_2019@data$Total, na.color = 'transparent')

leaflet()%>%
  addProviderTiles("CartoDB")%>%
  # First layer of polygons
  addPolygons(
    data = Total_spdf_file_2022,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Critical Violation : ', Total),
    group = '2022',
    highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
  ) %>%
  # Second layer of polygons
  addPolygons(
    data = Total_spdf_file_2021,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Violation : ', Total),
    group = '2021',
    highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
  ) %>%
  addLayersControl(overlayGroups = c("2022", "2021")) %>%
  # Third layer of polygons
  addPolygons(
    data = Total_spdf_file_2020,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Violation : ', Total),
    group = '2020',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront = TRUE)
  ) %>%
  # Fourth layer of polygons
  addPolygons(
    data = Total_spdf_file_2019,
    weight = 0.5,
    color = "black",
    stroke = TRUE,
    opacity = 1,
    fillColor = ~nc_pal(Total),
    label = ~paste0 ('Total Violation : ', Total),
    group = '2019',
    highlight = highlightOptions(weight  = 3, color = "red", bringToFront =  T)
  ) %>%
  addLayersControl(overlayGroups = c('2022', '2021', '2020', '2019')) %>%
  addLegend(pal = nc_pal, values = Total_spdf_file_2022$Total, opacity = 0.9, title = "Count of Total Violation", position = "bottomleft")
Warning: Some values were outside the color scale and will be treated as NAWarning: Some values were outside the color scale and will be treated as NA
violations <- list(total_violation,critical_violations)
names(violations) <- c("Number of Total Violations","Number of Crital Violations")
# Restaurant grade info
gradeInfo <- data %>%
  filter(!is.na(score)) %>%
  group_by(dba) %>%
  filter(inspection_date == max(inspection_date)) %>%
  select(dba, phone, grade, longitude, latitude, cuisine_description, zipcode, boro) %>%
  distinct(dba,.keep_all = TRUE) %>%
  ungroup()

gradeInfo$latitude <- as.numeric(gradeInfo$latitude)
gradeInfo$longitude <- as.numeric(gradeInfo$longitude)

score visualization

df$score <- as.numeric(df$score)
score_total <- df %>%
  filter(!is.na(score))%>%
  group_by(zipcode)%>%
  summarise(mean_score = mean(score))

score_2019 <- df%>%
  filter(!is.na(score)&year==2019)%>%
  group_by(zipcode)%>%
  summarise(mean_score = mean(score))
score_2019$mean_score <- round(score_2019$mean_score,2)

score_2020 <- df%>%
  filter(!is.na(score)&year==2020)%>%
  group_by(zipcode)%>%
  summarise(mean_score = mean(score))
score_2020$mean_score <- round(score_2020$mean_score,2)

score_2021 <- df%>%
  filter(!is.na(score)&year==2021)%>%
  group_by(zipcode)%>%
  summarise(mean_score = mean(score))
score_2021$mean_score <- round(score_2021$mean_score,2)

score_2022 <- df%>%
  filter(!is.na(score)&year==2022)%>%
  group_by(zipcode)%>%
  summarise(mean_score = mean(score))
score_2022$mean_score <- round(score_2022$mean_score,2)


Score_spdf_file_2019 <- spdf_file
Score_spdf_file_2019@data <- Score_spdf_file_2019@data %>%
  left_join(score_2019, c("ZIPCODE" = "zipcode"))

Score_spdf_file_2020 <- spdf_file
Score_spdf_file_2020@data <- Score_spdf_file_2020@data %>%
  left_join(score_2020, c("ZIPCODE" = "zipcode"))

Score_spdf_file_2021 <- spdf_file
Score_spdf_file_2021@data <- Score_spdf_file_2021@data %>%
  left_join(score_2021, c("ZIPCODE" = "zipcode"))

Score_spdf_file_2022 <- spdf_file
Score_spdf_file_2022@data <- Score_spdf_file_2022@data %>%
  left_join(score_2022, c("ZIPCODE" = "zipcode"))

score_map <- list(Score_spdf_file_2019,Score_spdf_file_2020,Score_spdf_file_2021,Score_spdf_file_2022)
names(score_map) <- c("2019","2020","2021","2022")

Section III: Shiny app

nc_pal <- colorNumeric(palette ="YlOrBr", domain = Total_spdf_file_2022@data$Total, na.color = 'transparent')
borough_list <- append("Overall", unique(df$boro))
cuisine_list <- append("Overall", unique(df$cuisine_description))

ui <- navbarPage(
  theme = bs_theme(bootswatch = "litera"), 
  "Restaurant Inspectation",
  tabPanel("Introduction"),
  tabPanel("Filtered Plots",
    fluidRow(
      column(3,
        selectInput("borough", "Borough", borough_list[!borough_list %in% c("0")], selected = "Overall"),
        selectInput("cuisine", "Cuisine", cuisine_list, selected = "Overall")
      ),
      column(9, 
        plotOutput("plot_action"),
        plotOutput("plot_critical_level"),
        plotOutput("plot_top_5_violation")
      )
    )
  ),
  navbarMenu("Violations Visualization",
            tabPanel("Violations Map",
    fluidRow(
      column(3,
        selectInput("type", "Type of Violations",c("Number of Total Violations", "Number of Crital Violations")),
        selectInput("time", "Year", c("2019", "2020", "2021", "2022"))
      ),
      column(9,
        leafletOutput("map", height = 600)
      )
    )
  ),
    
  tabPanel("Comparison by Years",
    fluidRow(
      column(4,
        selectInput("type_comp", "Type of Violations", c("Number of Total Violations", "Number of Crital Violations"))
      ),
      column(4,
        selectInput("time1", "Year", c("2019", "2020", "2021", "2022"))
      ),
      column(4,
        selectInput("time2", "Year", c("2019", "2020", "2021", "2022"), selected = "2020")
      )
    ),
    
    fluidRow(
      column(6,
        leafletOutput("map_comp1", height = 600)
      ),
      column(6,
        leafletOutput("map_comp2", height = 600)
      )
    )
  )),
  tabPanel("Inspection Score Visualization",
           fluidRow(column(3,
                  selectInput("score_year","Year:", c("2019", "2020", "2021", "2022"))),
           column(9, leafletOutput("score_map", height=600)))),
 
  tabPanel("Reference")
)

server <- function(input, output,session){
  # Filtered plots
  output$plot_action <- renderPlot({
    if (input$borough == 'Overall' & input$cuisine == 'Overall') {
      df_action <- df
    } else if (input$borough != 'Overall' & input$cuisine == 'Overall') {
      df_action <- df %>%
        filter(boro == input$borough)
    } else if (input$borough == 'Overall' & input$cuisine != 'Overall') {
      df_action <- df %>%
        filter(cuisine_description == input$cuisine)
    } else {
      df_action <- df %>%
        filter(boro == input$borough,
               cuisine_description == input$cuisine)
    }
    
    df_action %>%
      group_by(year, action) %>%
      summarize(num_violations = n()) %>%
      ggplot(aes(x = year, y = num_violations, fill = action)) +
      geom_bar(stat = "identity") +
      labs(title = "Number of Actions over Time", x = "Year", y = "Count", fill = "Action") +
      scale_fill_manual(values = wes_palettes$Moonrise3[c(3, 4, 5, 1, 2)]) +
      theme_minimal() +
      theme(legend.position = "bottom") +
      guides(fill = guide_legend(nrow = 5, byrow = TRUE))
  })
  
  output$plot_critical_level <- renderPlot({
    if (input$borough == 'Overall' & input$cuisine == 'Overall') {
      df_critical <- df
    } else if (input$borough != 'Overall' & input$cuisine == 'Overall') {
      df_critical <- df %>%
        filter(boro == input$borough)
    } else if (input$borough == 'Overall' & input$cuisine != 'Overall') {
      df_critical <- df %>%
        filter(cuisine_description == input$cuisine)
    } else {
      df_critical <- df %>%
        filter(boro == input$borough,
               cuisine_description == input$cuisine)
    }
    
    df_critical %>%
      filter(critical_flag != "Not Applicable") %>%
      group_by(year, critical_flag) %>%
      summarize(num_violations = n()) %>%
      ggplot(aes(x = year, y = num_violations, fill = critical_flag)) +
      geom_bar(stat = "identity") +
      labs(title = "Number of Violations by Critical Level over Time", x = "Year", y = "Count", fill = "Critical Level") +
      theme_minimal() +
      theme(legend.position = "bottom")
  })
  
  output$plot_top_5_violation <- renderPlot({
    if (input$borough == 'Overall' & input$cuisine == 'Overall') {
      df_top_5 <- df
    } else if (input$borough != 'Overall' & input$cuisine == 'Overall') {
      df_top_5 <- df %>%
        filter(boro == input$borough)
    } else if (input$borough == 'Overall' & input$cuisine != 'Overall') {
      df_top_5 <- df %>%
        filter(cuisine_description == input$cuisine)
    } else {
      df_top_5 <- df %>%
        filter(boro == input$borough,
               cuisine_description == input$cuisine)
    }
    
    df_top_5 %>%
      group_by(year, violation_code) %>%
      summarize(num_violations = n()) %>%
      ungroup() %>%
      group_by(year) %>%
      slice_max(num_violations, n = 5) %>%
      ggplot(aes(x = year, y = num_violations, fill = violation_code)) +
      geom_bar(stat = "identity") +
      labs(title = "Top 5 Violations over Time", x = "Year", y = "Count", fill = "Violation Code") +
      scale_fill_brewer(palette = "Set2") +
      theme_minimal() +
      theme(legend.position = "bottom")
  })
    
  # Interactive map
  output$map <- renderLeaflet({
    leaflet() %>%
    addProviderTiles("CartoDB") %>%
    addPolygons(
      data = violations[[input$type]][[input$time]],
      weight = 0.5,
      color = "black",
      stroke = TRUE,
      fillOpacity = 1,
      fillColor = ~nc_pal(Total),
      label = ~paste0 ('Total Violations : ', Total),
      group = '2022',
      highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
    ) %>%
    addLegend(pal = nc_pal, values = violations[[input$type]][[input$time]]$Total, opacity = 0.9, title = "Count of Total Violation", position = "bottomleft" )
  })
  
  # Interactive map compared by year  
  output$map_comp1 <- renderLeaflet({
    leaflet() %>%
    addProviderTiles("CartoDB") %>%
    addPolygons(
      data = violations[[input$type_comp]][[input$time1]],
      weight = 0.5,
      color = "black",
      stroke = TRUE,
      opacity = 1,
      fillOpacity = 1,
      fillColor = ~nc_pal(Total),
      label = ~paste0 ('Total Violations : ', Total),
      group = '2022',
      highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
    ) %>%
    addLegend(pal = nc_pal, values= violations[[input$type]][[input$time1]]$Total, opacity=0.9, title = "Count of Total Violation", position = "bottomleft" )
    })
  
  output$map_comp2 <- renderLeaflet({
    leaflet() %>%
    addProviderTiles("CartoDB") %>%
    addPolygons(
      data = violations[[input$type_comp]][[input$time2]],
      weight = 0.5,
      color = "black",
      stroke = TRUE,
      opacity = 1,
      fillOpacity = 1,
      fillColor = ~nc_pal(Total),
      label = ~paste0 ('Total Violations : ', Total),
      group = '2022',
      highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
    ) %>%
    addLegend(pal = nc_pal, values = violations[[input$type]][[input$time2]]$Total, opacity = 0.9, title = "Count of Total Violation", position = "bottomleft" )
  })
  
  # Interactive score map
  output$score_map <- renderLeaflet({
    nc_pal <- colorNumeric(palette ="Greens", domain = score_map[[3]]@data$mean_score, na.color = 'transparent')
    leaflet() %>%
    addProviderTiles("CartoDB") %>%
    addPolygons(
      data = score_map[[input$score_year]],
      weight = 0.5,
      color = "black",
      stroke = TRUE,
      fillOpacity = 1,
      fillColor = ~nc_pal(mean_score),
      label = ~paste0 ('Mean Inspection Score: ', mean_score),
      group = '2022',
      highlight = highlightOptions(weight = 3, color = "red", bringToFront = TRUE)
    ) %>%
    addLegend(pal = nc_pal, values = score_map[[input$score_year]]$mean_score, opacity = 0.9, title = "Mean Score", position = "bottomleft" )
  })
}

shinyApp(ui,server)

Listening on http://127.0.0.1:5166
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayIKb3V0cHV0OiBodG1sX25vdGVib29rCnJ1bnRpbWU6IHNoaW55Ci0tLQoKYGBge3J9CmxpYnJhcnkoYnJvb20pCmxpYnJhcnkoYnNsaWIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkoZ2VvanNvbmlvKQpsaWJyYXJ5KGdnbmV3c2NhbGUpCmxpYnJhcnkoZ2dwbG90MikKbGlicmFyeShsZWFmbGV0KQpsaWJyYXJ5KG9zbWRhdGEpCmxpYnJhcnkoUkNvbG9yQnJld2VyKQpsaWJyYXJ5KFJTb2NyYXRhKQpsaWJyYXJ5KHJzdHVkaW9hcGkpCmxpYnJhcnkoc2YpCmxpYnJhcnkoc2hpbnkpCmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHdlc2FuZGVyc29uKQoKIyBTZXQgd29ya2luZyBkaXJlY3RvcnkKY3VycmVudF9wYXRoIDwtIHJzdHVkaW9hcGk6OmdldEFjdGl2ZURvY3VtZW50Q29udGV4dCgpJHBhdGgKc2V0d2QoZGlybmFtZShjdXJyZW50X3BhdGgpKQpnZXR3ZCgpCmBgYAoKYGBge3J9CiMgTG9hZCB0aGUgcmVzdGF1cmFudCBkYXRhc2V0CmRhdGEgPC0gcmVhZC5zb2NyYXRhKAogICJodHRwczovL2RhdGEuY2l0eW9mbmV3eW9yay51cy9yZXNvdXJjZS80M25uLXBuOGouanNvbiIsCiAgYXBwX3Rva2VuID0gInpUUmVocDE4OTdTUXRwWXRCaUlPVU1mUjQiCikKCiMgRXh0cmFjdCB5ZWFycwpkYXRhJHllYXIgPC0gZm9ybWF0KGRhdGEkaW5zcGVjdGlvbl9kYXRlLCIlWSIpCgojIEZpbHRlciB0aGUgZGF0YXNldApkZiA8LSBkYXRhICU+JQogIGZpbHRlcihkYXRhJHllYXIgPj0gMjAxOSAmIHppcGNvZGUgIT0gIiIgJiBkYmEgIT0gIiIpICU+JQogIG11dGF0ZShncmFkZSA9IHJlcGxhY2UoZ3JhZGUsIGdyYWRlID09ICIiLCBOQSkpCgpoZWFkKGRmKQpgYGAKCmBgYHtyfQojIFJlYWQgdGhlIGdlb2pzb24gZmlsZSBjb250YWluaW5nIHNwYXRpYWwgaW5mbwpzcGRmX2ZpbGUgPC0gZ2VvanNvbl9yZWFkKCIuLi9kYXRhL3ppcF9jb2RlXzA0MDExNC5nZW9qc29uIiwgd2hhdCA9ICJzcCIpCgpzdGF0c19kZiA8LSBzcGRmX2ZpbGVAZGF0YQoKIyBDb252ZXJ0IGl0IHRvIGEgc3BhdGlhbCBkYXRhIGZyYW1lLCB3aXRoIHppcCBjb2RlIGFzIGluZGV4CnNwZGZfZGF0YSA8LSB0aWR5KHNwZGZfZmlsZSwKICByZWdpb24gPSAiWklQQ09ERSIgICMgVXNlIFpJUENPREUgdmFyaWFibGUgYXMgaW5kZXgsIHRoZSBpbmRleCB3aWxsIGJlIG5hbWVkICJpZCIKKQpgYGAKCiMgU2VjdGlvbiBJOiBGaWx0ZXJlZCBwbG90cwoKYGBge3J9CiMgTnVtYmVyIG9mIGFjdGlvbnMgb3ZlciB0aW1lCmRmICU+JQogIGdyb3VwX2J5KHllYXIsIGFjdGlvbikgJT4lCiAgc3VtbWFyaXplKG51bV92aW9sYXRpb25zID0gbigpKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbnVtX3Zpb2xhdGlvbnMsIGZpbGwgPSBhY3Rpb24pKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBBY3Rpb25zIG92ZXIgVGltZSIsIHggPSAiWWVhciIsIHkgPSAiQ291bnQiLCBmaWxsID0gIkFjdGlvbiIpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSB3ZXNfcGFsZXR0ZXMkTW9vbnJpc2UzW2MoMywgNCwgNSwgMSwgMildKSArCiAgdGhlbWVfbWluaW1hbCgpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikgKwogIGd1aWRlcyhmaWxsID0gZ3VpZGVfbGVnZW5kKG5yb3cgPSA1LCBieXJvdyA9IFRSVUUpKQogIAojIE51bWJlciBvZiB2aW9sYXRpb25zIGJ5IGNyaXRpY2FsIGxldmVsIG92ZXIgdGltZQpkZiAlPiUKICBmaWx0ZXIoY3JpdGljYWxfZmxhZyAhPSAiTm90IEFwcGxpY2FibGUiKSAlPiUKICBncm91cF9ieSh5ZWFyLCBjcml0aWNhbF9mbGFnKSAlPiUKICBzdW1tYXJpemUobnVtX3Zpb2xhdGlvbnMgPSBuKCkpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBudW1fdmlvbGF0aW9ucywgZmlsbCA9IGNyaXRpY2FsX2ZsYWcpKSArCiAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBWaW9sYXRpb25zIGJ5IENyaXRpY2FsIExldmVsIG92ZXIgVGltZSIsIHggPSAiWWVhciIsIHkgPSAiQ291bnQiLCBmaWxsID0gIkNyaXRpY2FsIExldmVsIikgKwogIHRoZW1lX21pbmltYWwoKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpCgojIFRvcCA1IHZpb2xhdGlvbnMKZGYgJT4lCiAgZ3JvdXBfYnkoeWVhciwgdmlvbGF0aW9uX2NvZGUpICU+JQogIHN1bW1hcml6ZShudW1fdmlvbGF0aW9ucyA9IG4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGdyb3VwX2J5KHllYXIpICU+JQogIHNsaWNlX21heChudW1fdmlvbGF0aW9ucywgbiA9IDUpICU+JQogIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBudW1fdmlvbGF0aW9ucywgZmlsbCA9IHZpb2xhdGlvbl9jb2RlKSkgKwogIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgbGFicyh0aXRsZSA9ICJUb3AgNSBWaW9sYXRpb25zIG92ZXIgVGltZSIsIHggPSAiWWVhciIsIHkgPSAiQ291bnQiLCBmaWxsID0gIlZpb2xhdGlvbiBDb2RlIikgKwogIHNjYWxlX2ZpbGxfYnJld2VyKHBhbGV0dGUgPSAiU2V0MiIpICsKICB0aGVtZV9taW5pbWFsKCkgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbiA9ICJib3R0b20iKQpgYGAKCiMgU2VjdGlvbiBJSTogSW50ZXJhY3RpdmUgcGxvdHMKCmBgYHtyfQojIE5vbi1pbnRlcmFjdGl2ZSBtYXAgKHBvcHVsYXRpb24gYnkgcmVnaW9uKQpnZ3Bsb3QoKSArCiAgZ2VvbV9wb2x5Z29uKGRhdGEgPSBzcGRmX2RhdGEgJT4lIGxlZnRfam9pbihzdGF0c19kZiwgYygiaWQiID0gIlpJUENPREUiKSksCiAgICAgICAgICAgICAgIGFlcyh4ID0gbG9uZywgeSA9IGxhdCwgZ3JvdXAgPSBncm91cCwgZmlsbCA9IFBPUFVMQVRJT04pLAogICAgICAgICAgICAgICBjb2xvciA9ICJ3aGl0ZSIsIHNpemUgPSAuMikgKwogIHRoZW1lX3ZvaWQoKSArCiAgY29vcmRfbWFwKCkgKwogIHNjYWxlX2ZpbGxfZGlzdGlsbGVyKHBhbGV0dGUgPSAiWWxHbkJ1IiwgZGlyZWN0aW9uID0gMSkgKwogIGxhYnModGl0bGUgPSAiUG9wdWxhdGlvbiBpbiBOZXcgWW9yayBDaXR5IiwKICAgICAgIHN1YnRpdGxlID0gIk5laWdoYm9yaG9vZHMgYXJlIGZpbGxlZCBieSBwb3B1bGF0aW9uIiwKICAgICAgIGZpbGwgPSAiUG9wdWxhdGlvbiIpCmBgYAoKYGBge3J9CiMgTnVtYmVyIG9mIHJlc3RhdXJhbnRzIHBlciB6aXBjb2RlCk51bV9SZXN0X0NvZGUgPC0gZGYgJT4lCiAgZ3JvdXBfYnkoemlwY29kZSwgZGJhLCBsYXRpdHVkZSwgbG9uZ2l0dWRlKSAlPiUKICBjb3VudCgpICU+JQogIGdyb3VwX2J5KHppcGNvZGUpICU+JQogIGNvdW50KCkKCkNyaXRpY2FsXzIwMTlfYnlfQ29kZSA8LSBkZiAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDE5KSAlPiUKICBncm91cF9ieSh6aXBjb2RlKSAlPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCiAgCkNyaXRpY2FsXzIwMjBfYnlfQ29kZSA8LSBkZiAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDIwKSAlPiUKICBncm91cF9ieSh6aXBjb2RlKSAlPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCiAgCkNyaXRpY2FsXzIwMjFfYnlfQ29kZSA8LSBkZiAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDIxKSAlPiUKICBncm91cF9ieSh6aXBjb2RlKSAlPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCiAgCkNyaXRpY2FsXzIwMjJfYnlfQ29kZSA8LSBkZiAlPiUKICBmaWx0ZXIoeWVhciA9PSAyMDIyKSAlPiUKICBncm91cF9ieSh6aXBjb2RlKSAlPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCgpDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMiA8LSBzcGRmX2ZpbGUKQ3JpdGljYWxfc3BkZl9maWxlXzIwMjJAZGF0YSA8LSBDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMkBkYXRhICU+JQogIGxlZnRfam9pbihDcml0aWNhbF8yMDIyX2J5X0NvZGUsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDE5IDwtIHNwZGZfZmlsZQpDcml0aWNhbF9zcGRmX2ZpbGVfMjAxOUBkYXRhIDwtIENyaXRpY2FsX3NwZGZfZmlsZV8yMDE5QGRhdGEgJT4lCiAgbGVmdF9qb2luKENyaXRpY2FsXzIwMTlfYnlfQ29kZSwgYygiWklQQ09ERSIgPSAiemlwY29kZSIpKQoKQ3JpdGljYWxfc3BkZl9maWxlXzIwMjAgPC0gc3BkZl9maWxlCkNyaXRpY2FsX3NwZGZfZmlsZV8yMDIwQGRhdGEgPC0gQ3JpdGljYWxfc3BkZl9maWxlXzIwMjBAZGF0YSAlPiUKICBsZWZ0X2pvaW4oQ3JpdGljYWxfMjAyMF9ieV9Db2RlLCBjKCJaSVBDT0RFIiA9ICJ6aXBjb2RlIikpCgpDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMSA8LSBzcGRmX2ZpbGUKQ3JpdGljYWxfc3BkZl9maWxlXzIwMjFAZGF0YSA8LSBDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMUBkYXRhICU+JQogIGxlZnRfam9pbihDcml0aWNhbF8yMDIxX2J5X0NvZGUsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKCmNyaXRpY2FsX3Zpb2xhdGlvbnMgPC0gbGlzdChDcml0aWNhbF9zcGRmX2ZpbGVfMjAxOSwgQ3JpdGljYWxfc3BkZl9maWxlXzIwMjAsIENyaXRpY2FsX3NwZGZfZmlsZV8yMDIxLCBDcml0aWNhbF9zcGRmX2ZpbGVfMjAyMikKbmFtZXMoY3JpdGljYWxfdmlvbGF0aW9ucykgPC0gYygiMjAxOSIsICIyMDIwIiwgIjIwMjEiLCAiMjAyMiIpCgpuY19wYWwgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSAiWWxPckJyIiwgZG9tYWluID0gQ3JpdGljYWxfc3BkZl9maWxlXzIwMTlAZGF0YSRUb3RhbCwgbmEuY29sb3IgPSAndHJhbnNwYXJlbnQnKQoKbGVhZmxldCgpICU+JQogIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIiKSAlPiUKICAjIEZpcnN0IGxheWVyIG9mIHBvbHlnb25zCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gQ3JpdGljYWxfc3BkZl9maWxlXzIwMjIsCiAgICB3ZWlnaHQgPSAwLjUsCiAgICBjb2xvciA9ICJibGFjayIsCiAgICBzdHJva2UgPSBUUlVFLAogICAgb3BhY2l0eSA9IDEsCiAgICBmaWxsQ29sb3IgPSB+bmNfcGFsKFRvdGFsKSwKICAgIGxhYmVsID0gfnBhc3RlMCgnVG90YWwgQ3JpdGljYWwgVmlvbGF0aW9uIDogJywgVG90YWwpLAogICAgZ3JvdXAgPSAnMjAyMicsCiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgKSAlPiUKICAjIFNlY29uZCBsYXllciBvZiBwb2x5Z29ucwogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IENyaXRpY2FsX3NwZGZfZmlsZV8yMDIxLAogICAgd2VpZ2h0ID0gMC41LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc3Ryb2tlID0gVFJVRSwKICAgIG9wYWNpdHkgPSAxLAogICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICBsYWJlbCA9IH5wYXN0ZTAgKCdUb3RhbCBDcml0aWNhbCBWaW9sYXRpb24gOiAnLCBUb3RhbCksCiAgICBncm91cCA9ICcyMDIxJywKICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ID0gMywgY29sb3IgPSAicmVkIiwgYnJpbmdUb0Zyb250ID0gIFRSVUUpCiAgKSAlPiUKICBhZGRMYXllcnNDb250cm9sKG92ZXJsYXlHcm91cHMgPSBjKCIyMDIyIiwgIjIwMjEiKSkgJT4lCiAgYWRkTGVnZW5kKHBhbCA9IG5jX3BhbCwgdmFsdWVzID0gQ3JpdGljYWxfc3BkZl9maWxlXzIwMjIkVG90YWwsIG9wYWNpdHkgPSAwLjksIHRpdGxlID0gIkNyaXRpY2FsIiwgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIpCmBgYAoKYGBge3J9ClRvdGFsX3Zpb2xhdGlvbiA8LSBkZiAlPiUKICBncm91cF9ieSh6aXBjb2RlKSAlPiUKICBzdW1tYXJpemUoVG90YWwgPSBuKCkpCgpUb3RhbF8yMDE5X2J5X0NvZGUgPC0gZGYgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAxOSkgJT4lCiAgZ3JvdXBfYnkoemlwY29kZSkgJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQogIApUb3RhbF8yMDIwX2J5X0NvZGUgPC0gZGYgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAyMCkgJT4lCiAgZ3JvdXBfYnkoemlwY29kZSkgJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQogIApUb3RhbF8yMDIxX2J5X0NvZGUgPC0gZGYgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAyMSkgJT4lCiAgZ3JvdXBfYnkoemlwY29kZSkgJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQogIApUb3RhbF8yMDIyX2J5X0NvZGUgPC0gZGYgJT4lCiAgZmlsdGVyKHllYXIgPT0gMjAyMikgJT4lCiAgZ3JvdXBfYnkoemlwY29kZSkgJT4lCiAgc3VtbWFyaXplKFRvdGFsID0gbigpKQoKVG90YWxfc3BkZl9maWxlXzIwMjIgPC0gc3BkZl9maWxlClRvdGFsX3NwZGZfZmlsZV8yMDIyQGRhdGEgPC0gVG90YWxfc3BkZl9maWxlXzIwMjJAZGF0YSAlPiUKICBsZWZ0X2pvaW4oVG90YWxfMjAyMl9ieV9Db2RlLCBjKCJaSVBDT0RFIiA9ICJ6aXBjb2RlIikpCgpUb3RhbF9zcGRmX2ZpbGVfMjAxOSA8LSBzcGRmX2ZpbGUKVG90YWxfc3BkZl9maWxlXzIwMTlAZGF0YSA8LSBUb3RhbF9zcGRmX2ZpbGVfMjAxOUBkYXRhICU+JQogIGxlZnRfam9pbihUb3RhbF8yMDE5X2J5X0NvZGUsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKClRvdGFsX3NwZGZfZmlsZV8yMDIwIDwtIHNwZGZfZmlsZQpUb3RhbF9zcGRmX2ZpbGVfMjAyMEBkYXRhIDwtIFRvdGFsX3NwZGZfZmlsZV8yMDIwQGRhdGEgJT4lCiAgbGVmdF9qb2luKFRvdGFsXzIwMjBfYnlfQ29kZSwgYygiWklQQ09ERSIgPSAiemlwY29kZSIpKQoKVG90YWxfc3BkZl9maWxlXzIwMjEgPC0gc3BkZl9maWxlClRvdGFsX3NwZGZfZmlsZV8yMDIxQGRhdGEgPC0gVG90YWxfc3BkZl9maWxlXzIwMjFAZGF0YSAlPiUKICBsZWZ0X2pvaW4oVG90YWxfMjAyMV9ieV9Db2RlLCBjKCJaSVBDT0RFIiA9ICJ6aXBjb2RlIikpCgp0b3RhbF92aW9sYXRpb24gPC0gbGlzdChUb3RhbF9zcGRmX2ZpbGVfMjAxOSwgVG90YWxfc3BkZl9maWxlXzIwMjAsIFRvdGFsX3NwZGZfZmlsZV8yMDIxLCBUb3RhbF9zcGRmX2ZpbGVfMjAyMikKbmFtZXModG90YWxfdmlvbGF0aW9uKSA8LSBjKCIyMDE5IiwgIjIwMjAiLCAiMjAyMSIsICIyMDIyIikKCm5jX3BhbCA8LSBjb2xvck51bWVyaWMocGFsZXR0ZSA9ICJZbE9yQnIiLCBkb21haW4gPSBUb3RhbF9zcGRmX2ZpbGVfMjAxOUBkYXRhJFRvdGFsLCBuYS5jb2xvciA9ICd0cmFuc3BhcmVudCcpCgpsZWFmbGV0KCklPiUKICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCIiklPiUKICAjIEZpcnN0IGxheWVyIG9mIHBvbHlnb25zCiAgYWRkUG9seWdvbnMoCiAgICBkYXRhID0gVG90YWxfc3BkZl9maWxlXzIwMjIsCiAgICB3ZWlnaHQgPSAwLjUsCiAgICBjb2xvciA9ICJibGFjayIsCiAgICBzdHJva2UgPSBUUlVFLAogICAgb3BhY2l0eSA9IDEsCiAgICBmaWxsQ29sb3IgPSB+bmNfcGFsKFRvdGFsKSwKICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIENyaXRpY2FsIFZpb2xhdGlvbiA6ICcsIFRvdGFsKSwKICAgIGdyb3VwID0gJzIwMjInLAogICAgaGlnaGxpZ2h0ID0gaGlnaGxpZ2h0T3B0aW9ucyh3ZWlnaHQgPSAzLCBjb2xvciA9ICJyZWQiLCBicmluZ1RvRnJvbnQgPSBUUlVFKQogICkgJT4lCiAgIyBTZWNvbmQgbGF5ZXIgb2YgcG9seWdvbnMKICBhZGRQb2x5Z29ucygKICAgIGRhdGEgPSBUb3RhbF9zcGRmX2ZpbGVfMjAyMSwKICAgIHdlaWdodCA9IDAuNSwKICAgIGNvbG9yID0gImJsYWNrIiwKICAgIHN0cm9rZSA9IFRSVUUsCiAgICBvcGFjaXR5ID0gMSwKICAgIGZpbGxDb2xvciA9IH5uY19wYWwoVG90YWwpLAogICAgbGFiZWwgPSB+cGFzdGUwICgnVG90YWwgVmlvbGF0aW9uIDogJywgVG90YWwpLAogICAgZ3JvdXAgPSAnMjAyMScsCiAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgKSAlPiUKICBhZGRMYXllcnNDb250cm9sKG92ZXJsYXlHcm91cHMgPSBjKCIyMDIyIiwgIjIwMjEiKSkgJT4lCiAgIyBUaGlyZCBsYXllciBvZiBwb2x5Z29ucwogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IFRvdGFsX3NwZGZfZmlsZV8yMDIwLAogICAgd2VpZ2h0ID0gMC41LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc3Ryb2tlID0gVFJVRSwKICAgIG9wYWNpdHkgPSAxLAogICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICBsYWJlbCA9IH5wYXN0ZTAgKCdUb3RhbCBWaW9sYXRpb24gOiAnLCBUb3RhbCksCiAgICBncm91cCA9ICcyMDIwJywKICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ICA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgKSAlPiUKICAjIEZvdXJ0aCBsYXllciBvZiBwb2x5Z29ucwogIGFkZFBvbHlnb25zKAogICAgZGF0YSA9IFRvdGFsX3NwZGZfZmlsZV8yMDE5LAogICAgd2VpZ2h0ID0gMC41LAogICAgY29sb3IgPSAiYmxhY2siLAogICAgc3Ryb2tlID0gVFJVRSwKICAgIG9wYWNpdHkgPSAxLAogICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICBsYWJlbCA9IH5wYXN0ZTAgKCdUb3RhbCBWaW9sYXRpb24gOiAnLCBUb3RhbCksCiAgICBncm91cCA9ICcyMDE5JywKICAgIGhpZ2hsaWdodCA9IGhpZ2hsaWdodE9wdGlvbnMod2VpZ2h0ICA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9ICBUKQogICkgJT4lCiAgYWRkTGF5ZXJzQ29udHJvbChvdmVybGF5R3JvdXBzID0gYygnMjAyMicsICcyMDIxJywgJzIwMjAnLCAnMjAxOScpKSAlPiUKICBhZGRMZWdlbmQocGFsID0gbmNfcGFsLCB2YWx1ZXMgPSBUb3RhbF9zcGRmX2ZpbGVfMjAyMiRUb3RhbCwgb3BhY2l0eSA9IDAuOSwgdGl0bGUgPSAiQ291bnQgb2YgVG90YWwgVmlvbGF0aW9uIiwgcG9zaXRpb24gPSAiYm90dG9tbGVmdCIpCgp2aW9sYXRpb25zIDwtIGxpc3QodG90YWxfdmlvbGF0aW9uLGNyaXRpY2FsX3Zpb2xhdGlvbnMpCm5hbWVzKHZpb2xhdGlvbnMpIDwtIGMoIk51bWJlciBvZiBUb3RhbCBWaW9sYXRpb25zIiwiTnVtYmVyIG9mIENyaXRhbCBWaW9sYXRpb25zIikKYGBgCgpgYGB7cn0KIyBSZXN0YXVyYW50IGdyYWRlIGluZm8KZ3JhZGVJbmZvIDwtIGRhdGEgJT4lCiAgZmlsdGVyKCFpcy5uYShzY29yZSkpICU+JQogIGdyb3VwX2J5KGRiYSkgJT4lCiAgZmlsdGVyKGluc3BlY3Rpb25fZGF0ZSA9PSBtYXgoaW5zcGVjdGlvbl9kYXRlKSkgJT4lCiAgc2VsZWN0KGRiYSwgcGhvbmUsIGdyYWRlLCBsb25naXR1ZGUsIGxhdGl0dWRlLCBjdWlzaW5lX2Rlc2NyaXB0aW9uLCB6aXBjb2RlLCBib3JvKSAlPiUKICBkaXN0aW5jdChkYmEsLmtlZXBfYWxsID0gVFJVRSkgJT4lCiAgdW5ncm91cCgpCgpncmFkZUluZm8kbGF0aXR1ZGUgPC0gYXMubnVtZXJpYyhncmFkZUluZm8kbGF0aXR1ZGUpCmdyYWRlSW5mbyRsb25naXR1ZGUgPC0gYXMubnVtZXJpYyhncmFkZUluZm8kbG9uZ2l0dWRlKQpgYGAKCgojIyMjIHNjb3JlIHZpc3VhbGl6YXRpb24KYGBge3J9CmRmJHNjb3JlIDwtIGFzLm51bWVyaWMoZGYkc2NvcmUpCnNjb3JlX3RvdGFsIDwtIGRmICU+JQogIGZpbHRlcighaXMubmEoc2NvcmUpKSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXNlKG1lYW5fc2NvcmUgPSBtZWFuKHNjb3JlKSkKCnNjb3JlXzIwMTkgPC0gZGYlPiUKICBmaWx0ZXIoIWlzLm5hKHNjb3JlKSZ5ZWFyPT0yMDE5KSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXNlKG1lYW5fc2NvcmUgPSBtZWFuKHNjb3JlKSkKc2NvcmVfMjAxOSRtZWFuX3Njb3JlIDwtIHJvdW5kKHNjb3JlXzIwMTkkbWVhbl9zY29yZSwyKQoKc2NvcmVfMjAyMCA8LSBkZiU+JQogIGZpbHRlcighaXMubmEoc2NvcmUpJnllYXI9PTIwMjApJT4lCiAgZ3JvdXBfYnkoemlwY29kZSklPiUKICBzdW1tYXJpc2UobWVhbl9zY29yZSA9IG1lYW4oc2NvcmUpKQpzY29yZV8yMDIwJG1lYW5fc2NvcmUgPC0gcm91bmQoc2NvcmVfMjAyMCRtZWFuX3Njb3JlLDIpCgpzY29yZV8yMDIxIDwtIGRmJT4lCiAgZmlsdGVyKCFpcy5uYShzY29yZSkmeWVhcj09MjAyMSklPiUKICBncm91cF9ieSh6aXBjb2RlKSU+JQogIHN1bW1hcmlzZShtZWFuX3Njb3JlID0gbWVhbihzY29yZSkpCnNjb3JlXzIwMjEkbWVhbl9zY29yZSA8LSByb3VuZChzY29yZV8yMDIxJG1lYW5fc2NvcmUsMikKCnNjb3JlXzIwMjIgPC0gZGYlPiUKICBmaWx0ZXIoIWlzLm5hKHNjb3JlKSZ5ZWFyPT0yMDIyKSU+JQogIGdyb3VwX2J5KHppcGNvZGUpJT4lCiAgc3VtbWFyaXNlKG1lYW5fc2NvcmUgPSBtZWFuKHNjb3JlKSkKc2NvcmVfMjAyMiRtZWFuX3Njb3JlIDwtIHJvdW5kKHNjb3JlXzIwMjIkbWVhbl9zY29yZSwyKQoKClNjb3JlX3NwZGZfZmlsZV8yMDE5IDwtIHNwZGZfZmlsZQpTY29yZV9zcGRmX2ZpbGVfMjAxOUBkYXRhIDwtIFNjb3JlX3NwZGZfZmlsZV8yMDE5QGRhdGEgJT4lCiAgbGVmdF9qb2luKHNjb3JlXzIwMTksIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKClNjb3JlX3NwZGZfZmlsZV8yMDIwIDwtIHNwZGZfZmlsZQpTY29yZV9zcGRmX2ZpbGVfMjAyMEBkYXRhIDwtIFNjb3JlX3NwZGZfZmlsZV8yMDIwQGRhdGEgJT4lCiAgbGVmdF9qb2luKHNjb3JlXzIwMjAsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKClNjb3JlX3NwZGZfZmlsZV8yMDIxIDwtIHNwZGZfZmlsZQpTY29yZV9zcGRmX2ZpbGVfMjAyMUBkYXRhIDwtIFNjb3JlX3NwZGZfZmlsZV8yMDIxQGRhdGEgJT4lCiAgbGVmdF9qb2luKHNjb3JlXzIwMjEsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKClNjb3JlX3NwZGZfZmlsZV8yMDIyIDwtIHNwZGZfZmlsZQpTY29yZV9zcGRmX2ZpbGVfMjAyMkBkYXRhIDwtIFNjb3JlX3NwZGZfZmlsZV8yMDIyQGRhdGEgJT4lCiAgbGVmdF9qb2luKHNjb3JlXzIwMjIsIGMoIlpJUENPREUiID0gInppcGNvZGUiKSkKCnNjb3JlX21hcCA8LSBsaXN0KFNjb3JlX3NwZGZfZmlsZV8yMDE5LFNjb3JlX3NwZGZfZmlsZV8yMDIwLFNjb3JlX3NwZGZfZmlsZV8yMDIxLFNjb3JlX3NwZGZfZmlsZV8yMDIyKQpuYW1lcyhzY29yZV9tYXApIDwtIGMoIjIwMTkiLCIyMDIwIiwiMjAyMSIsIjIwMjIiKQpgYGAKCgojIFNlY3Rpb24gSUlJOiBTaGlueSBhcHAKCmBgYHtyfQpuY19wYWwgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSJZbE9yQnIiLCBkb21haW4gPSBUb3RhbF9zcGRmX2ZpbGVfMjAyMkBkYXRhJFRvdGFsLCBuYS5jb2xvciA9ICd0cmFuc3BhcmVudCcpCmJvcm91Z2hfbGlzdCA8LSBhcHBlbmQoIk92ZXJhbGwiLCB1bmlxdWUoZGYkYm9ybykpCmN1aXNpbmVfbGlzdCA8LSBhcHBlbmQoIk92ZXJhbGwiLCB1bmlxdWUoZGYkY3Vpc2luZV9kZXNjcmlwdGlvbikpCgp1aSA8LSBuYXZiYXJQYWdlKAogIHRoZW1lID0gYnNfdGhlbWUoYm9vdHN3YXRjaCA9ICJsaXRlcmEiKSwgCiAgIlJlc3RhdXJhbnQgSW5zcGVjdGF0aW9uIiwKICB0YWJQYW5lbCgiSW50cm9kdWN0aW9uIiksCiAgdGFiUGFuZWwoIkZpbHRlcmVkIFBsb3RzIiwKICAgIGZsdWlkUm93KAogICAgICBjb2x1bW4oMywKICAgICAgICBzZWxlY3RJbnB1dCgiYm9yb3VnaCIsICJCb3JvdWdoIiwgYm9yb3VnaF9saXN0WyFib3JvdWdoX2xpc3QgJWluJSBjKCIwIildLCBzZWxlY3RlZCA9ICJPdmVyYWxsIiksCiAgICAgICAgc2VsZWN0SW5wdXQoImN1aXNpbmUiLCAiQ3Vpc2luZSIsIGN1aXNpbmVfbGlzdCwgc2VsZWN0ZWQgPSAiT3ZlcmFsbCIpCiAgICAgICksCiAgICAgIGNvbHVtbig5LCAKICAgICAgICBwbG90T3V0cHV0KCJwbG90X2FjdGlvbiIpLAogICAgICAgIHBsb3RPdXRwdXQoInBsb3RfY3JpdGljYWxfbGV2ZWwiKSwKICAgICAgICBwbG90T3V0cHV0KCJwbG90X3RvcF81X3Zpb2xhdGlvbiIpCiAgICAgICkKICAgICkKICApLAogIG5hdmJhck1lbnUoIlZpb2xhdGlvbnMgVmlzdWFsaXphdGlvbiIsCiAgICAgICAgICAgIHRhYlBhbmVsKCJWaW9sYXRpb25zIE1hcCIsCiAgICBmbHVpZFJvdygKICAgICAgY29sdW1uKDMsCiAgICAgICAgc2VsZWN0SW5wdXQoInR5cGUiLCAiVHlwZSBvZiBWaW9sYXRpb25zIixjKCJOdW1iZXIgb2YgVG90YWwgVmlvbGF0aW9ucyIsICJOdW1iZXIgb2YgQ3JpdGFsIFZpb2xhdGlvbnMiKSksCiAgICAgICAgc2VsZWN0SW5wdXQoInRpbWUiLCAiWWVhciIsIGMoIjIwMTkiLCAiMjAyMCIsICIyMDIxIiwgIjIwMjIiKSkKICAgICAgKSwKICAgICAgY29sdW1uKDksCiAgICAgICAgbGVhZmxldE91dHB1dCgibWFwIiwgaGVpZ2h0ID0gNjAwKQogICAgICApCiAgICApCiAgKSwKICAgIAogIHRhYlBhbmVsKCJDb21wYXJpc29uIGJ5IFllYXJzIiwKICAgIGZsdWlkUm93KAogICAgICBjb2x1bW4oNCwKICAgICAgICBzZWxlY3RJbnB1dCgidHlwZV9jb21wIiwgIlR5cGUgb2YgVmlvbGF0aW9ucyIsIGMoIk51bWJlciBvZiBUb3RhbCBWaW9sYXRpb25zIiwgIk51bWJlciBvZiBDcml0YWwgVmlvbGF0aW9ucyIpKQogICAgICApLAogICAgICBjb2x1bW4oNCwKICAgICAgICBzZWxlY3RJbnB1dCgidGltZTEiLCAiWWVhciIsIGMoIjIwMTkiLCAiMjAyMCIsICIyMDIxIiwgIjIwMjIiKSkKICAgICAgKSwKICAgICAgY29sdW1uKDQsCiAgICAgICAgc2VsZWN0SW5wdXQoInRpbWUyIiwgIlllYXIiLCBjKCIyMDE5IiwgIjIwMjAiLCAiMjAyMSIsICIyMDIyIiksIHNlbGVjdGVkID0gIjIwMjAiKQogICAgICApCiAgICApLAogICAgCiAgICBmbHVpZFJvdygKICAgICAgY29sdW1uKDYsCiAgICAgICAgbGVhZmxldE91dHB1dCgibWFwX2NvbXAxIiwgaGVpZ2h0ID0gNjAwKQogICAgICApLAogICAgICBjb2x1bW4oNiwKICAgICAgICBsZWFmbGV0T3V0cHV0KCJtYXBfY29tcDIiLCBoZWlnaHQgPSA2MDApCiAgICAgICkKICAgICkKICApKSwKICB0YWJQYW5lbCgiSW5zcGVjdGlvbiBTY29yZSBWaXN1YWxpemF0aW9uIiwKICAgICAgICAgICBmbHVpZFJvdyhjb2x1bW4oMywKICAgICAgICAgICAgICAgICAgc2VsZWN0SW5wdXQoInNjb3JlX3llYXIiLCJZZWFyOiIsIGMoIjIwMTkiLCAiMjAyMCIsICIyMDIxIiwgIjIwMjIiKSkpLAogICAgICAgICAgIGNvbHVtbig5LCBsZWFmbGV0T3V0cHV0KCJzY29yZV9tYXAiLCBoZWlnaHQ9NjAwKSkpKSwKIAogIHRhYlBhbmVsKCJSZWZlcmVuY2UiKQopCgpzZXJ2ZXIgPC0gZnVuY3Rpb24oaW5wdXQsIG91dHB1dCxzZXNzaW9uKXsKICAjIEZpbHRlcmVkIHBsb3RzCiAgb3V0cHV0JHBsb3RfYWN0aW9uIDwtIHJlbmRlclBsb3QoewogICAgaWYgKGlucHV0JGJvcm91Z2ggPT0gJ092ZXJhbGwnICYgaW5wdXQkY3Vpc2luZSA9PSAnT3ZlcmFsbCcpIHsKICAgICAgZGZfYWN0aW9uIDwtIGRmCiAgICB9IGVsc2UgaWYgKGlucHV0JGJvcm91Z2ggIT0gJ092ZXJhbGwnICYgaW5wdXQkY3Vpc2luZSA9PSAnT3ZlcmFsbCcpIHsKICAgICAgZGZfYWN0aW9uIDwtIGRmICU+JQogICAgICAgIGZpbHRlcihib3JvID09IGlucHV0JGJvcm91Z2gpCiAgICB9IGVsc2UgaWYgKGlucHV0JGJvcm91Z2ggPT0gJ092ZXJhbGwnICYgaW5wdXQkY3Vpc2luZSAhPSAnT3ZlcmFsbCcpIHsKICAgICAgZGZfYWN0aW9uIDwtIGRmICU+JQogICAgICAgIGZpbHRlcihjdWlzaW5lX2Rlc2NyaXB0aW9uID09IGlucHV0JGN1aXNpbmUpCiAgICB9IGVsc2UgewogICAgICBkZl9hY3Rpb24gPC0gZGYgJT4lCiAgICAgICAgZmlsdGVyKGJvcm8gPT0gaW5wdXQkYm9yb3VnaCwKICAgICAgICAgICAgICAgY3Vpc2luZV9kZXNjcmlwdGlvbiA9PSBpbnB1dCRjdWlzaW5lKQogICAgfQogICAgCiAgICBkZl9hY3Rpb24gJT4lCiAgICAgIGdyb3VwX2J5KHllYXIsIGFjdGlvbikgJT4lCiAgICAgIHN1bW1hcml6ZShudW1fdmlvbGF0aW9ucyA9IG4oKSkgJT4lCiAgICAgIGdncGxvdChhZXMoeCA9IHllYXIsIHkgPSBudW1fdmlvbGF0aW9ucywgZmlsbCA9IGFjdGlvbikpICsKICAgICAgZ2VvbV9iYXIoc3RhdCA9ICJpZGVudGl0eSIpICsKICAgICAgbGFicyh0aXRsZSA9ICJOdW1iZXIgb2YgQWN0aW9ucyBvdmVyIFRpbWUiLCB4ID0gIlllYXIiLCB5ID0gIkNvdW50IiwgZmlsbCA9ICJBY3Rpb24iKSArCiAgICAgIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IHdlc19wYWxldHRlcyRNb29ucmlzZTNbYygzLCA0LCA1LCAxLCAyKV0pICsKICAgICAgdGhlbWVfbWluaW1hbCgpICsKICAgICAgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gImJvdHRvbSIpICsKICAgICAgZ3VpZGVzKGZpbGwgPSBndWlkZV9sZWdlbmQobnJvdyA9IDUsIGJ5cm93ID0gVFJVRSkpCiAgfSkKICAKICBvdXRwdXQkcGxvdF9jcml0aWNhbF9sZXZlbCA8LSByZW5kZXJQbG90KHsKICAgIGlmIChpbnB1dCRib3JvdWdoID09ICdPdmVyYWxsJyAmIGlucHV0JGN1aXNpbmUgPT0gJ092ZXJhbGwnKSB7CiAgICAgIGRmX2NyaXRpY2FsIDwtIGRmCiAgICB9IGVsc2UgaWYgKGlucHV0JGJvcm91Z2ggIT0gJ092ZXJhbGwnICYgaW5wdXQkY3Vpc2luZSA9PSAnT3ZlcmFsbCcpIHsKICAgICAgZGZfY3JpdGljYWwgPC0gZGYgJT4lCiAgICAgICAgZmlsdGVyKGJvcm8gPT0gaW5wdXQkYm9yb3VnaCkKICAgIH0gZWxzZSBpZiAoaW5wdXQkYm9yb3VnaCA9PSAnT3ZlcmFsbCcgJiBpbnB1dCRjdWlzaW5lICE9ICdPdmVyYWxsJykgewogICAgICBkZl9jcml0aWNhbCA8LSBkZiAlPiUKICAgICAgICBmaWx0ZXIoY3Vpc2luZV9kZXNjcmlwdGlvbiA9PSBpbnB1dCRjdWlzaW5lKQogICAgfSBlbHNlIHsKICAgICAgZGZfY3JpdGljYWwgPC0gZGYgJT4lCiAgICAgICAgZmlsdGVyKGJvcm8gPT0gaW5wdXQkYm9yb3VnaCwKICAgICAgICAgICAgICAgY3Vpc2luZV9kZXNjcmlwdGlvbiA9PSBpbnB1dCRjdWlzaW5lKQogICAgfQogICAgCiAgICBkZl9jcml0aWNhbCAlPiUKICAgICAgZmlsdGVyKGNyaXRpY2FsX2ZsYWcgIT0gIk5vdCBBcHBsaWNhYmxlIikgJT4lCiAgICAgIGdyb3VwX2J5KHllYXIsIGNyaXRpY2FsX2ZsYWcpICU+JQogICAgICBzdW1tYXJpemUobnVtX3Zpb2xhdGlvbnMgPSBuKCkpICU+JQogICAgICBnZ3Bsb3QoYWVzKHggPSB5ZWFyLCB5ID0gbnVtX3Zpb2xhdGlvbnMsIGZpbGwgPSBjcml0aWNhbF9mbGFnKSkgKwogICAgICBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IikgKwogICAgICBsYWJzKHRpdGxlID0gIk51bWJlciBvZiBWaW9sYXRpb25zIGJ5IENyaXRpY2FsIExldmVsIG92ZXIgVGltZSIsIHggPSAiWWVhciIsIHkgPSAiQ291bnQiLCBmaWxsID0gIkNyaXRpY2FsIExldmVsIikgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICB9KQogIAogIG91dHB1dCRwbG90X3RvcF81X3Zpb2xhdGlvbiA8LSByZW5kZXJQbG90KHsKICAgIGlmIChpbnB1dCRib3JvdWdoID09ICdPdmVyYWxsJyAmIGlucHV0JGN1aXNpbmUgPT0gJ092ZXJhbGwnKSB7CiAgICAgIGRmX3RvcF81IDwtIGRmCiAgICB9IGVsc2UgaWYgKGlucHV0JGJvcm91Z2ggIT0gJ092ZXJhbGwnICYgaW5wdXQkY3Vpc2luZSA9PSAnT3ZlcmFsbCcpIHsKICAgICAgZGZfdG9wXzUgPC0gZGYgJT4lCiAgICAgICAgZmlsdGVyKGJvcm8gPT0gaW5wdXQkYm9yb3VnaCkKICAgIH0gZWxzZSBpZiAoaW5wdXQkYm9yb3VnaCA9PSAnT3ZlcmFsbCcgJiBpbnB1dCRjdWlzaW5lICE9ICdPdmVyYWxsJykgewogICAgICBkZl90b3BfNSA8LSBkZiAlPiUKICAgICAgICBmaWx0ZXIoY3Vpc2luZV9kZXNjcmlwdGlvbiA9PSBpbnB1dCRjdWlzaW5lKQogICAgfSBlbHNlIHsKICAgICAgZGZfdG9wXzUgPC0gZGYgJT4lCiAgICAgICAgZmlsdGVyKGJvcm8gPT0gaW5wdXQkYm9yb3VnaCwKICAgICAgICAgICAgICAgY3Vpc2luZV9kZXNjcmlwdGlvbiA9PSBpbnB1dCRjdWlzaW5lKQogICAgfQogICAgCiAgICBkZl90b3BfNSAlPiUKICAgICAgZ3JvdXBfYnkoeWVhciwgdmlvbGF0aW9uX2NvZGUpICU+JQogICAgICBzdW1tYXJpemUobnVtX3Zpb2xhdGlvbnMgPSBuKCkpICU+JQogICAgICB1bmdyb3VwKCkgJT4lCiAgICAgIGdyb3VwX2J5KHllYXIpICU+JQogICAgICBzbGljZV9tYXgobnVtX3Zpb2xhdGlvbnMsIG4gPSA1KSAlPiUKICAgICAgZ2dwbG90KGFlcyh4ID0geWVhciwgeSA9IG51bV92aW9sYXRpb25zLCBmaWxsID0gdmlvbGF0aW9uX2NvZGUpKSArCiAgICAgIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiKSArCiAgICAgIGxhYnModGl0bGUgPSAiVG9wIDUgVmlvbGF0aW9ucyBvdmVyIFRpbWUiLCB4ID0gIlllYXIiLCB5ID0gIkNvdW50IiwgZmlsbCA9ICJWaW9sYXRpb24gQ29kZSIpICsKICAgICAgc2NhbGVfZmlsbF9icmV3ZXIocGFsZXR0ZSA9ICJTZXQyIikgKwogICAgICB0aGVtZV9taW5pbWFsKCkgKwogICAgICB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAiYm90dG9tIikKICB9KQogICAgCiAgIyBJbnRlcmFjdGl2ZSBtYXAKICBvdXRwdXQkbWFwIDwtIHJlbmRlckxlYWZsZXQoewogICAgbGVhZmxldCgpICU+JQogICAgYWRkUHJvdmlkZXJUaWxlcygiQ2FydG9EQiIpICU+JQogICAgYWRkUG9seWdvbnMoCiAgICAgIGRhdGEgPSB2aW9sYXRpb25zW1tpbnB1dCR0eXBlXV1bW2lucHV0JHRpbWVdXSwKICAgICAgd2VpZ2h0ID0gMC41LAogICAgICBjb2xvciA9ICJibGFjayIsCiAgICAgIHN0cm9rZSA9IFRSVUUsCiAgICAgIGZpbGxPcGFjaXR5ID0gMSwKICAgICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnLCBUb3RhbCksCiAgICAgIGdyb3VwID0gJzIwMjInLAogICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgICApICU+JQogICAgYWRkTGVnZW5kKHBhbCA9IG5jX3BhbCwgdmFsdWVzID0gdmlvbGF0aW9uc1tbaW5wdXQkdHlwZV1dW1tpbnB1dCR0aW1lXV0kVG90YWwsIG9wYWNpdHkgPSAwLjksIHRpdGxlID0gIkNvdW50IG9mIFRvdGFsIFZpb2xhdGlvbiIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKICB9KQogIAogICMgSW50ZXJhY3RpdmUgbWFwIGNvbXBhcmVkIGJ5IHllYXIgIAogIG91dHB1dCRtYXBfY29tcDEgPC0gcmVuZGVyTGVhZmxldCh7CiAgICBsZWFmbGV0KCkgJT4lCiAgICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCIikgJT4lCiAgICBhZGRQb2x5Z29ucygKICAgICAgZGF0YSA9IHZpb2xhdGlvbnNbW2lucHV0JHR5cGVfY29tcF1dW1tpbnB1dCR0aW1lMV1dLAogICAgICB3ZWlnaHQgPSAwLjUsCiAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgb3BhY2l0eSA9IDEsCiAgICAgIGZpbGxPcGFjaXR5ID0gMSwKICAgICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnLCBUb3RhbCksCiAgICAgIGdyb3VwID0gJzIwMjInLAogICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgICApICU+JQogICAgYWRkTGVnZW5kKHBhbCA9IG5jX3BhbCwgdmFsdWVzPSB2aW9sYXRpb25zW1tpbnB1dCR0eXBlXV1bW2lucHV0JHRpbWUxXV0kVG90YWwsIG9wYWNpdHk9MC45LCB0aXRsZSA9ICJDb3VudCBvZiBUb3RhbCBWaW9sYXRpb24iLCBwb3NpdGlvbiA9ICJib3R0b21sZWZ0IiApCiAgICB9KQogIAogIG91dHB1dCRtYXBfY29tcDIgPC0gcmVuZGVyTGVhZmxldCh7CiAgICBsZWFmbGV0KCkgJT4lCiAgICBhZGRQcm92aWRlclRpbGVzKCJDYXJ0b0RCIikgJT4lCiAgICBhZGRQb2x5Z29ucygKICAgICAgZGF0YSA9IHZpb2xhdGlvbnNbW2lucHV0JHR5cGVfY29tcF1dW1tpbnB1dCR0aW1lMl1dLAogICAgICB3ZWlnaHQgPSAwLjUsCiAgICAgIGNvbG9yID0gImJsYWNrIiwKICAgICAgc3Ryb2tlID0gVFJVRSwKICAgICAgb3BhY2l0eSA9IDEsCiAgICAgIGZpbGxPcGFjaXR5ID0gMSwKICAgICAgZmlsbENvbG9yID0gfm5jX3BhbChUb3RhbCksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ1RvdGFsIFZpb2xhdGlvbnMgOiAnLCBUb3RhbCksCiAgICAgIGdyb3VwID0gJzIwMjInLAogICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgICApICU+JQogICAgYWRkTGVnZW5kKHBhbCA9IG5jX3BhbCwgdmFsdWVzID0gdmlvbGF0aW9uc1tbaW5wdXQkdHlwZV1dW1tpbnB1dCR0aW1lMl1dJFRvdGFsLCBvcGFjaXR5ID0gMC45LCB0aXRsZSA9ICJDb3VudCBvZiBUb3RhbCBWaW9sYXRpb24iLCBwb3NpdGlvbiA9ICJib3R0b21sZWZ0IiApCiAgfSkKICAKICAjIEludGVyYWN0aXZlIHNjb3JlIG1hcAogIG91dHB1dCRzY29yZV9tYXAgPC0gcmVuZGVyTGVhZmxldCh7CiAgICBuY19wYWwgPC0gY29sb3JOdW1lcmljKHBhbGV0dGUgPSJHcmVlbnMiLCBkb21haW4gPSBzY29yZV9tYXBbWzNdXUBkYXRhJG1lYW5fc2NvcmUsIG5hLmNvbG9yID0gJ3RyYW5zcGFyZW50JykKICAgIGxlYWZsZXQoKSAlPiUKICAgIGFkZFByb3ZpZGVyVGlsZXMoIkNhcnRvREIiKSAlPiUKICAgIGFkZFBvbHlnb25zKAogICAgICBkYXRhID0gc2NvcmVfbWFwW1tpbnB1dCRzY29yZV95ZWFyXV0sCiAgICAgIHdlaWdodCA9IDAuNSwKICAgICAgY29sb3IgPSAiYmxhY2siLAogICAgICBzdHJva2UgPSBUUlVFLAogICAgICBmaWxsT3BhY2l0eSA9IDEsCiAgICAgIGZpbGxDb2xvciA9IH5uY19wYWwobWVhbl9zY29yZSksCiAgICAgIGxhYmVsID0gfnBhc3RlMCAoJ01lYW4gSW5zcGVjdGlvbiBTY29yZTogJywgbWVhbl9zY29yZSksCiAgICAgIGdyb3VwID0gJzIwMjInLAogICAgICBoaWdobGlnaHQgPSBoaWdobGlnaHRPcHRpb25zKHdlaWdodCA9IDMsIGNvbG9yID0gInJlZCIsIGJyaW5nVG9Gcm9udCA9IFRSVUUpCiAgICApICU+JQogICAgYWRkTGVnZW5kKHBhbCA9IG5jX3BhbCwgdmFsdWVzID0gc2NvcmVfbWFwW1tpbnB1dCRzY29yZV95ZWFyXV0kbWVhbl9zY29yZSwgb3BhY2l0eSA9IDAuOSwgdGl0bGUgPSAiTWVhbiBTY29yZSIsIHBvc2l0aW9uID0gImJvdHRvbWxlZnQiICkKICB9KQp9CgpzaGlueUFwcCh1aSxzZXJ2ZXIpCmBgYA==